/**
 * Cesium碰撞检测系统
 * 基于Mapbox的核心算法，适配Cesium的坐标系统和渲染管线
 */

import * as Cesium from 'cesium';

// 常量定义
const VIEWPORT_PADDING = 100; // 视口填充，像素
const GRID_CELL_SIZE = 25;    // 网格单元大小，像素
const MIN_PERSPECTIVE_RATIO = 0.55; // 最小透视比率
const CIRCLE_DISTANCE_MULTIPLIER = 2.5; // 圆形间距倍数

/**
 * 二维点类
 */
class Point2D {
  constructor(x = 0, y = 0) {
    this.x = x;
    this.y = y;
  }

  dist (other) {
    const dx = this.x - other.x;
    const dy = this.y - other.y;
    return Math.sqrt(dx * dx + dy * dy);
  }

  distSqr (other) {
    const dx = this.x - other.x;
    const dy = this.y - other.y;
    return dx * dx + dy * dy;
  }

  add (other) {
    return new Point2D(this.x + other.x, this.y + other.y);
  }

  sub (other) {
    return new Point2D(this.x - other.x, this.y - other.y);
  }

  mult (scalar) {
    return new Point2D(this.x * scalar, this.y * scalar);
  }

  rotate (angle) {
    const cos = Math.cos(angle);
    const sin = Math.sin(angle);
    return new Point2D(
      this.x * cos - this.y * sin,
      this.x * sin + this.y * cos
    );
  }

  clone () {
    return new Point2D(this.x, this.y);
  }
}

/**
 * 空间网格索引
 */
class GridIndex {
  constructor(width, height, cellSize = GRID_CELL_SIZE) {
    this.width = width;
    this.height = height;
    this.cellSize = cellSize;

    this.xCellCount = Math.ceil(width / cellSize);
    this.yCellCount = Math.ceil(height / cellSize);
    this.xScale = this.xCellCount / width;
    this.yScale = this.yCellCount / height;

    // 初始化网格单元
    this.boxCells = [];
    this.circleCells = [];
    const totalCells = this.xCellCount * this.yCellCount;

    for (let i = 0; i < totalCells; i++) {
      this.boxCells.push([]);
      this.circleCells.push([]);
    }

    // 存储几何体数据
    this.boxKeys = [];
    this.circleKeys = [];
    this.bboxes = [];
    this.circles = [];
    this.boxUid = 0;
    this.circleUid = 0;
  }

  /**
   * 插入矩形碰撞体
   */
  insertBox (key, x1, y1, x2, y2) {
    this._forEachCell(x1, y1, x2, y2, (cellIndex) => {
      this.boxCells[cellIndex].push(this.boxUid);
    });

    this.boxKeys.push(key);
    this.bboxes.push(x1, y1, x2, y2);
    this.boxUid++;
  }

  /**
   * 插入圆形碰撞体
   */
  insertCircle (key, x, y, radius) {
    this._forEachCell(x - radius, y - radius, x + radius, y + radius, (cellIndex) => {
      this.circleCells[cellIndex].push(this.circleUid);
    });

    this.circleKeys.push(key);
    this.circles.push(x, y, radius);
    this.circleUid++;
  }

  /**
   * 矩形碰撞测试
   */
  hitTestBox (x1, y1, x2, y2, predicate = null) {
    if (x2 < 0 || x1 > this.width || y2 < 0 || y1 > this.height) {
      return false;
    }

    const seenUids = new Set();
    let collision = false;

    this._forEachCell(x1, y1, x2, y2, (cellIndex) => {
      // 检查矩形碰撞
      for (const uid of this.boxCells[cellIndex]) {
        if (seenUids.has(uid)) continue;
        seenUids.add(uid);

        const key = this.boxKeys[uid];
        if (predicate && !predicate(key)) continue;

        const bx1 = this.bboxes[uid * 4];
        const by1 = this.bboxes[uid * 4 + 1];
        const bx2 = this.bboxes[uid * 4 + 2];
        const by2 = this.bboxes[uid * 4 + 3];

        if (!(x2 < bx1 || x1 > bx2 || y2 < by1 || y1 > by2)) {
          collision = true;
          return true; // 提前退出
        }
      }

      // 检查与圆形的碰撞
      for (const uid of this.circleCells[cellIndex]) {
        if (seenUids.has(`circle_${uid}`)) continue;
        seenUids.add(`circle_${uid}`);

        const key = this.circleKeys[uid];
        if (predicate && !predicate(key)) continue;

        const cx = this.circles[uid * 3];
        const cy = this.circles[uid * 3 + 1];
        const radius = this.circles[uid * 3 + 2];

        if (this._boxCircleCollide(x1, y1, x2, y2, cx, cy, radius)) {
          collision = true;
          return true; // 提前退出
        }
      }
    });

    return collision;
  }

  /**
   * 圆形碰撞测试
   */
  hitTestCircle (x, y, radius, predicate = null) {
    const x1 = x - radius;
    const x2 = x + radius;
    const y1 = y - radius;
    const y2 = y + radius;

    if (x2 < 0 || x1 > this.width || y2 < 0 || y1 > this.height) {
      return false;
    }

    const seenUids = new Set();
    let collision = false;

    this._forEachCell(x1, y1, x2, y2, (cellIndex) => {
      // 检查圆形碰撞
      for (const uid of this.circleCells[cellIndex]) {
        if (seenUids.has(uid)) continue;
        seenUids.add(uid);

        const key = this.circleKeys[uid];
        if (predicate && !predicate(key)) continue;

        const cx = this.circles[uid * 3];
        const cy = this.circles[uid * 3 + 1];
        const cr = this.circles[uid * 3 + 2];

        const dx = x - cx;
        const dy = y - cy;
        const distSq = dx * dx + dy * dy;
        const radiusSum = radius + cr;

        if (distSq < radiusSum * radiusSum) {
          collision = true;
          return true; // 提前退出
        }
      }

      // 检查与矩形的碰撞
      for (const uid of this.boxCells[cellIndex]) {
        if (seenUids.has(`box_${uid}`)) continue;
        seenUids.add(`box_${uid}`);

        const key = this.boxKeys[uid];
        if (predicate && !predicate(key)) continue;

        const bx1 = this.bboxes[uid * 4];
        const by1 = this.bboxes[uid * 4 + 1];
        const bx2 = this.bboxes[uid * 4 + 2];
        const by2 = this.bboxes[uid * 4 + 3];

        if (this._boxCircleCollide(bx1, by1, bx2, by2, x, y, radius)) {
          collision = true;
          return true; // 提前退出
        }
      }
    });

    return collision;
  }

  /**
   * 清空网格
   */
  clear () {
    for (let i = 0; i < this.boxCells.length; i++) {
      this.boxCells[i].length = 0;
      this.circleCells[i].length = 0;
    }
    this.boxKeys.length = 0;
    this.circleKeys.length = 0;
    this.bboxes.length = 0;
    this.circles.length = 0;
    this.boxUid = 0;
    this.circleUid = 0;
  }

  /**
   * 遍历指定区域的所有网格单元
   */
  _forEachCell (x1, y1, x2, y2, callback) {
    const minX = Math.max(0, Math.floor(x1 * this.xScale));
    const minY = Math.max(0, Math.floor(y1 * this.yScale));
    const maxX = Math.min(this.xCellCount - 1, Math.floor(x2 * this.xScale));
    const maxY = Math.min(this.yCellCount - 1, Math.floor(y2 * this.yScale));

    for (let y = minY; y <= maxY; y++) {
      for (let x = minX; x <= maxX; x++) {
        const cellIndex = y * this.xCellCount + x;
        if (callback(cellIndex) === true) {
          return; // 提前退出
        }
      }
    }
  }

  /**
   * 矩形与圆形碰撞检测
   */
  _boxCircleCollide (x1, y1, x2, y2, cx, cy, radius) {
    const closestX = Math.max(x1, Math.min(cx, x2));
    const closestY = Math.max(y1, Math.min(cy, y2));
    const dx = cx - closestX;
    const dy = cy - closestY;
    return (dx * dx + dy * dy) < (radius * radius);
  }
}

/**
 * 路径插值器
 */
class PathInterpolator {
  constructor() {
    this.reset();
  }

  reset (points = [], padding = 0) {
    this.points = points;
    this.padding = padding;
    this._distances = [0];

    // 计算累积距离
    for (let i = 1; i < points.length; i++) {
      const dist = points[i].dist(points[i - 1]);
      this._distances[i] = this._distances[i - 1] + dist;
    }

    this.length = this._distances[this._distances.length - 1] || 0;
    this.paddedLength = Math.max(0, this.length - padding * 2);
  }

  /**
   * 线性插值获取路径上的点
   */
  lerp (t) {
    if (this.points.length === 0) return new Point2D();
    if (this.points.length === 1) return this.points[0].clone();

    t = Math.max(0, Math.min(1, t));
    const targetDist = t * this.paddedLength + this.padding;

    // 查找对应的线段
    let segmentIndex = 1;
    while (segmentIndex < this._distances.length && this._distances[segmentIndex] < targetDist) {
      segmentIndex++;
    }

    if (segmentIndex >= this._distances.length) {
      return this.points[this.points.length - 1].clone();
    }

    // 在线段内插值
    const prevIndex = segmentIndex - 1;
    const prevDist = this._distances[prevIndex];
    const currDist = this._distances[segmentIndex];
    const segmentLength = currDist - prevDist;

    if (segmentLength === 0) {
      return this.points[prevIndex].clone();
    }

    const segmentT = (targetDist - prevDist) / segmentLength;
    const p1 = this.points[prevIndex];
    const p2 = this.points[segmentIndex];

    return new Point2D(
      p1.x + (p2.x - p1.x) * segmentT,
      p1.y + (p2.y - p1.y) * segmentT
    );
  }
}

/**
 * 碰撞检测盒
 */
class CollisionBox {
  constructor(x1, y1, x2, y2, elevation = 0) {
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
    this.elevation = elevation;
    this.padding = 0;
  }

  /**
   * 添加填充
   */
  addPadding (padding) {
    this.padding = padding;
    return this;
  }

  /**
   * 获取带填充的边界
   */
  getPaddedBounds () {
    return {
      x1: this.x1 - this.padding,
      y1: this.y1 - this.padding,
      x2: this.x2 + this.padding,
      y2: this.y2 + this.padding
    };
  }
}

/**
 * Cesium碰撞检测器
 */
class CesiumCollisionIndex {
  constructor(viewer) {
    this.viewer = viewer;
    this.canvas = viewer.canvas;
    this.camera = viewer.camera;
    this.scene = viewer.scene;

    this.grid = null;
    this.ignoredGrid = null;

    this._initializeGrids();
    this._setupEventListeners();
  }

  /**
   * 初始化网格
   */
  _initializeGrids () {
    const width = this.canvas.clientWidth + 2 * VIEWPORT_PADDING;
    const height = this.canvas.clientHeight + 2 * VIEWPORT_PADDING;

    this.grid = new GridIndex(width, height, GRID_CELL_SIZE);
    this.ignoredGrid = new GridIndex(width, height, GRID_CELL_SIZE);
  }

  /**
   * 设置事件监听器
   */
  _setupEventListeners () {
    // 监听画布大小变化
    const resizeObserver = new ResizeObserver(() => {
      this._initializeGrids();
    });
    resizeObserver.observe(this.canvas);

    // 监听相机变化
    this.camera.changed.addEventListener(() => {
      this.clearCollisions();
    });
  }

  /**
   * 世界坐标转屏幕坐标
   */
  worldToScreen (cartographic) {
    const cartesian = Cesium.Cartesian3.fromRadians(
      cartographic.longitude,
      cartographic.latitude,
      cartographic.height || 0
    );

    const screenPosition = Cesium.SceneTransforms.worldToWindowCoordinates(
      this.scene,
      cartesian
    );

    if (!screenPosition) {
      return null;
    }

    return new Point2D(
      screenPosition.x + VIEWPORT_PADDING,
      screenPosition.y + VIEWPORT_PADDING
    );
  }

  /**
   * 计算透视比率
   */
  getPerspectiveRatio (cartesian) {
    const cameraPosition = this.camera.position;
    const distance = Cesium.Cartesian3.distance(cameraPosition, cartesian);
    const fovRatio = Math.tan(this.camera.frustum.fovy * 0.5);

    // 简化的透视比率计算
    const cameraToCenterDistance = Cesium.Cartesian3.magnitude(cameraPosition);
    return Math.min(0.5 + 0.5 * (cameraToCenterDistance / distance), 1.5);
  }

  /**
   * 检查点是否被遮挡
   */
  isOccluded (cartesian) {
    const occluder = this.scene.globe.ellipsoid.createOccluder(this.camera.position);
    return !occluder.isPointVisible(cartesian);
  }

  /**
   * 放置碰撞盒
   */
  placeCollisionBox (position, size, options = {}) {
    const {
      allowOverlap = false,
      scale = 1,
      padding = 0,
      priority = 0,
      groupId = 0
    } = options;

    // 转换坐标
    const screenPos = this.worldToScreen(position);
    if (!screenPos) {
      return { placed: false, reason: 'offscreen' };
    }

    // 计算屏幕空间的边界盒
    const halfWidth = (size.width * scale) / 2;
    const halfHeight = (size.height * scale) / 2;

    const box = new CollisionBox(
      screenPos.x - halfWidth,
      screenPos.y - halfHeight,
      screenPos.x + halfWidth,
      screenPos.y + halfHeight
    ).addPadding(padding);

    const bounds = box.getPaddedBounds();

    // 检查是否在视口内
    if (!this._isInsideViewport(bounds.x1, bounds.y1, bounds.x2, bounds.y2)) {
      return { placed: false, reason: 'outside_viewport' };
    }

    // 碰撞检测
    if (!allowOverlap) {
      const collision = this.grid.hitTestBox(
        bounds.x1, bounds.y1, bounds.x2, bounds.y2,
        (key) => key.groupId !== groupId
      );

      if (collision) {
        return { placed: false, reason: 'collision' };
      }
    }

    // 插入到网格
    const key = { groupId, priority, id: this._generateId() };
    this.grid.insertBox(key, bounds.x1, bounds.y1, bounds.x2, bounds.y2);

    return {
      placed: true,
      box: bounds,
      screenPosition: screenPos,
      key
    };
  }

  /**
   * 放置线性标签的碰撞圆
   */
  placeLineLabel (linePositions, options = {}) {
    const {
      fontSize = 16,
      padding = 2,
      allowOverlap = false,
      groupId = 0,
      showDebugCircles = false
    } = options;

    // 转换线性坐标到屏幕空间
    const screenPath = [];
    for (const pos of linePositions) {
      const screenPos = this.worldToScreen(pos);
      if (screenPos) {
        screenPath.push(screenPos);
      }
    }

    if (screenPath.length < 2) {
      return { placed: false, reason: 'insufficient_points' };
    }

    // 使用路径插值器
    const interpolator = new PathInterpolator();
    const radius = fontSize / 2 + padding;
    interpolator.reset(screenPath, radius * 0.25);

    if (interpolator.length === 0) {
      return { placed: false, reason: 'zero_length_path' };
    }

    // 计算碰撞圆的数量和位置
    const circleDistance = radius * CIRCLE_DISTANCE_MULTIPLIER;
    const numCircles = Math.max(1, Math.ceil(interpolator.paddedLength / circleDistance) + 1);
    const circles = [];
    let hasCollision = false;

    for (let i = 0; i < numCircles; i++) {
      const t = i / Math.max(numCircles - 1, 1);
      const circlePos = interpolator.lerp(t);

      // 检查碰撞
      if (!allowOverlap) {
        const collision = this.grid.hitTestCircle(
          circlePos.x, circlePos.y, radius,
          (key) => key.groupId !== groupId
        );

        if (collision) {
          hasCollision = true;
          if (!showDebugCircles) {
            break;
          }
        }
      }

      circles.push({
        x: circlePos.x,
        y: circlePos.y,
        radius: radius
      });
    }

    if (hasCollision && !showDebugCircles) {
      return { placed: false, reason: 'collision' };
    }

    // 插入所有圆到网格
    if (!hasCollision) {
      const key = { groupId, id: this._generateId() };
      for (const circle of circles) {
        this.grid.insertCircle(key, circle.x, circle.y, circle.radius);
      }
    }

    return {
      placed: !hasCollision,
      circles: circles,
      interpolator: interpolator
    };
  }

  /**
   * 清空所有碰撞体
   */
  clearCollisions () {
    if (this.grid) this.grid.clear();
    if (this.ignoredGrid) this.ignoredGrid.clear();
  }

  /**
   * 检查点是否在视口内
   */
  _isInsideViewport (x1, y1, x2, y2) {
    const width = this.canvas.clientWidth + 2 * VIEWPORT_PADDING;
    const height = this.canvas.clientHeight + 2 * VIEWPORT_PADDING;

    return x2 >= 0 && x1 < width && y2 >= 0 && y1 < height;
  }

  /**
   * 生成唯一ID
   */
  _generateId () {
    return Date.now() + Math.random();
  }

  /**
   * 获取网格统计信息
   */
  getStats () {
    return {
      boxCount: this.grid.boxKeys.length,
      circleCount: this.grid.circleKeys.length,
      gridSize: `${this.grid.xCellCount} x ${this.grid.yCellCount}`,
      canvasSize: `${this.canvas.clientWidth} x ${this.canvas.clientHeight}`
    };
  }
}

export { CesiumCollisionIndex, CollisionBox, Point2D, PathInterpolator }; 